package com.hqyj.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hqyj.mapper.UserMapper;
import com.hqyj.pojo.SelectAll;
import com.hqyj.pojo.User;
import com.hqyj.pojo.UserVO;
import com.hqyj.service.UserService;
import com.hqyj.utils.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements UserService {

    private static final int CACHE_LIFE_TIME = 60*10;


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Map<String, Object> register(String uAccount ,String uPassword , String uPhone ,String code) {


        //1、校验
        if (StringUtils.isAnyBlank(uAccount,uPassword,uPhone,code)){
            return ResultUtil.error("参数不能为空");
        }
        if (uAccount.length() < 4 || uPassword.length() < 4){
            return ResultUtil.error("账号密码长度不能小于4");
        }

        //账户不能包含特殊字符
        String validPattern = "[\\n`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。， 、？✅\\uD83D\\uDFE7]";
        Matcher matcher = Pattern.compile(validPattern).matcher(uAccount);

        if (matcher.find()){
            return ResultUtil.error("账号不能包含特殊字符");
        }

        //todo  校验手机号   还可以写在发送验证码
        //13、 14 、 15 、 16 、 17 、18 、 19开头的手机号
        validPattern = "/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$/";
        matcher = Pattern.compile(validPattern).matcher(uPhone);

        if (matcher.find()){
            return ResultUtil.error("非法手机号");
        }


        //todo 校验验证码 -- redis
        if (!redisUtil.hasKey(uPhone)){
            return ResultUtil.error("验证码已过期");
        }
        String redisCode = (String) redisUtil.get(uPhone);
        if (!redisCode.equals(code)){
            return ResultUtil.error("验证码错误!");
        }
        //验证码校验成功，就删了
        redisUtil.del(uPhone);

        //todo 账号不能重复 --查询数据库

        User byAccount = userMapper.selectOneByAccount(uAccount);
        if (byAccount != null){
            return ResultUtil.error("账号重复!");
        }

        //2、加密  --MD5
        String md5Password = MD5Util.md5(uPassword, uAccount);

        //3、向数据库插入数据 -- 插入md5密码
        userMapper.insertOne(uAccount,md5Password,uPhone);

        return ResultUtil.success("注册成功",uAccount);
    }

    @Override
    public Map<String, Object> getCode(String uPhone) {

        //校验
        if (StringUtils.isAnyBlank(uPhone)){
            return ResultUtil.error("手机号不能为空");
        }

        //todo  校验手机号   还可以写在发送验证码
        //13、 14 、 15 、 16 、 17 、18 、 19开头的手机号
        String validPattern = "/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$/";
        Matcher matcher = Pattern.compile(validPattern).matcher(uPhone);

        if (matcher.find()){
            return ResultUtil.error("非法手机号");
        }

        //生成验证码
        String code = ValidateCodeUtils.generateValidateCode(6) + "";
        System.out.println(code);
        //发送验证码到手机上
        SendSms.sendCode(uPhone,code);

        //放在redis里面,有效期5分钟
        redisUtil.set(uPhone,code,5*60);

        return ResultUtil.success("发送成功",null);
    }

    @Override
    public Map<String, Object> checkAccount(String uAccount) {
        User user = userMapper.selectOneByAccount(uAccount);
        if (user != null){
            return ResultUtil.error("已有重复账号");
        }
        return ResultUtil.success("无重复账号","null");
    }

    @Override
    public Map<String, Object> login(String uAccount, String uPassword, HttpServletRequest request)  {
        Map<String, Object> result = new HashMap<>();
        //1、校验
        if(StringUtils.isAnyBlank(uAccount,uPassword)){
            return ResultUtil.error("账号或密码不能为空");
        }
        if (uAccount.length() < 4 || uPassword.length() < 4){
            return ResultUtil.error("账号或密码不得少于4位");
        }

        //账号不能包含特殊字符
        String validPattern = "[\\n`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。， 、？✅\\uD83D\\uDFE7]";
        Matcher matcher = Pattern.compile(validPattern).matcher(uAccount);
        if (matcher.find()){
            return ResultUtil.error("账号不能包含特殊字符");
        }

        //获取主体, 代表当前用户的对象
        Subject subject = SecurityUtils.getSubject();
        if(!subject.isAuthenticated()){
            //没认证用户才登录
            //使用待认证的用户名和密码创建安全令牌对象
            AuthenticationToken token = new UsernamePasswordToken(uAccount, uPassword);
            //让shiro框架检查令牌，执行登录
            try{
                subject.login(token);
            }catch (UnknownAccountException e){
                //用户不存在
                result.put("code", -1);
                result.put("message", uAccount + "不存在");
                return result;
            }catch (IncorrectCredentialsException e){
                //用户名和密码不匹配
                result.put("code", -2);
                result.put("message", uAccount + "密码错误");
                return result;
            }catch (AuthenticationException e){
                //其他任何异常
                e.printStackTrace();
                result.put("code", -3);
                result.put("message", uAccount + "认证失败");
                return result;
            }
        }
        //已经通过认证
        System.out.println(uAccount + "认证通过了....");

        //从shiro提供session对象中获取已经认证成功的用户信息
        User user = (User) subject.getSession().getAttribute("user");
        if (user != null){
            userMapper.updateLastLoginTime(new Date(), user.getUId());
            redisUtil.set(uAccount,user);
            result.put("code", 200);
            result.put("message", uAccount +"登录成功");
        }else {
            result.put("code", 201);
            result.put("message", uAccount +"登录成功");
        }
        result.put("loginUser", user);
        return result;
        ////2、加密
        //String md5Password = MD5Util.md5(uPassword, uAccount);
        //
        ////3、查询账户是否存在
        //User user = userMapper.selectOneByAccountAndPassword(uAccount, md5Password);
        //if (user == null ){
        //    return ResultUtil.error("账号或密码错误");
        //}

        //4、将user保存到session
        // todo 脱敏
        //request.getSession().setAttribute("loginUser",user);

        //将user登录态保存到redis
        //key:account value:user

        //Integer result0 = userMapper.updateLastLoginTime(new Date(), user.getUId());
        //if (result0 != null && result0 != 0){
        //    return ResultUtil.success("登录成功",user);
        //}
        //return ResultUtil.error("登陆失败");

    }

    @Override
    public Integer updateLastLoginTime(Date date , int uId) {
        return userMapper.updateLastLoginTime(date,uId);
    }

    @Override
    public Map<String, Object> selectAll(int pageNum,int pageSize,String uName, String departmentName) {
        Map<String, Object> map = new HashMap<>();
        String key = "allUsers_page" + pageNum;

        if (!"".equals(uName) || !"".equals(departmentName)){
            PageHelper.startPage(pageNum,pageSize);
            List<SelectAll> selectAlls = userMapper.selectAll(uName,departmentName);
            PageInfo<SelectAll> selectAllPageInfo = new PageInfo<>(selectAlls);
            map.put("code",200);
            map.put("paging",selectAllPageInfo);
        }

        else if (!redisUtil.hasKey(key)){
            //如果缓存中没有，就查数据库

            //开启分页
            PageHelper.startPage(pageNum,pageSize);
            List<SelectAll> selectAlls = userMapper.selectAll(uName,departmentName);
            PageInfo<SelectAll> selectAllPageInfo = new PageInfo<>(selectAlls);
            map.put("code",200);
            map.put("paging",selectAllPageInfo);
            redisUtil.hmset(key,map,CACHE_LIFE_TIME);
            //System.out.println(map);
        }else {
            //缓存中有
            map = redisUtil.hmget(key);
        }

        return map;
    }

    //修改的信息回显
    @Override
    public Map<String, Object> update(int id) {
        UserVO userVO = userMapper.update(id);
        Map<String, Object> map = new HashMap<>();
        if (userVO!=null){
            map.put("code",200);
            map.put("updateInfo",userVO);
            return map;
        }else {
            map.put("code",500);
            return map;
        }
    }

    //修改用户信息
    @Override
    public Map<String, Object> updateUser(User user) {
        int i = userMapper.updateUser(user);
        Map<String, Object> map = new HashMap<>();
        if (i>0){
            delCache();
            redisUtil.del(redisUtil.keysLike("goodsAll"));
            map.put("code",200);
            return map;
        }else {
            map.put("code",500);
            return map;
        }
    }

    //删除用户
    @Override
    public Map<String, Object> deleteUser(int id) {
        int i = userMapper.deleteUser(id);
        Map<String, Object> map = new HashMap<>();
        if (userMapper.findGoodsContactByUidCount(id)>0){
            Integer ret = userMapper.updateGoodsContactByuId(id);
            if (ret>0){
                map.put("cascadeState",true);
            }else {
                map.put("cascadeState",false);
            }
        }
        if (i>0){
            delCache();
            redisUtil.del(redisUtil.keysLike("goodsAll"));
            map.put("code",200);
            return map;
        }else {
            map.put("code",500);
            return map;
        }
    }

    public void delCache(){
        redisUtil.del(redisUtil.keysLike("allUsers_page"));
    }

    // 将已登录的用户登出
    @Override
    public Map<String, Object> logout(String uAccount) {
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        Object username = subject.getPrincipal();
        //执行登出 , 即删除所有已经登录的相关信息
        subject.logout();
        redisUtil.del(uAccount);
        Map<String, Object> result = new HashMap<>();

        return ResultUtil.success("用户登出成功",null);
    }
}
